/*   Carina
 *   Copyright (C) 2009 2010 2011  Zdravko Velinov
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "property-editor.hh"
#include "property.hh"

#include "carina/utils.hh"

#include <QSpinBox>

Property::Property()
    :   m_Parent(0) {}

Property::~Property()
{
}

Property* Property::getParent()
{
    return m_Parent;
}

const Property* Property::getParent() const
{
    return m_Parent;
}

void Property::setParent(Property* parent)
{
    m_Parent = parent;
}

void Property::addChild(const PropertyPtr& prop)
{
    prop->setParent(this);
    m_Children.push_back(prop);
}

void Property::removeChildren(size_t lhs, size_t rhs)
{
    m_Children.erase(lhs, rhs);
}

const Property& Property::getChild(size_t idx) const
{
    return *m_Children[idx];
}

Property& Property::getChild(size_t idx)
{
    return *m_Children[idx];
}

void Property::clearChildren()
{
    m_Children.clear();
}

size_t Property::getChildrenCount() const
{
    return m_Children.size();
}

size_t Property::getIndex(Property* ptr) const
{
    for(size_t i = 0; i < m_Children.size(); ++i)
        if(m_Children[i].get() == ptr)
            return i;
    return std::numeric_limits<size_t>::max();
}

void Property::update()
{
    _changed();
}

PropertyEditor::PropertyEditor()
{
    m_PropEdit.setupUi(this);

    m_PropEdit.PropertyView->setModel(&m_PropModel);
    m_PropEdit.PropertyView->setItemDelegateForColumn(PROPERTY_VALUE, &m_PropDelegate);
}

template<class T>
struct CreateEditableProperty
{
	static shared_ptr<EditableProperty<T>> exec(CESAttribute* ces_attr, const AttributePtr& attr)
	{
		return make_aligned_shared<EditableProperty<T>>(*ces_attr, static_pointer_cast<TypedAttribute<T>>(attr));
	}
};

template<template <class U, class A> class TCont, class T, class TAlloc>
struct CreateEditableProperty<TCont<T, TAlloc>>
{
	static shared_ptr<EditableProperty<TCont<T, TAlloc>>> exec(CESAttribute* ces_attr, const AttributePtr& attr)
	{
		return make_aligned_shared<EditableProperty<TCont<T, TAlloc>>>(*ces_attr, static_pointer_cast<TypedAttribute<T>>(attr));
	}
};

void PropertyEditor::setEditable(const EditablePtr& editable)
{
    string node_type;
    m_PropModel.clear();
    if(!editable)
        return;
	auto attr_list = GetAttributeList(*editable->m_CarinaConfigurable);
    editable->m_CESAttributes.getAttribute("Node type", node_type);
    if(node_type == "Root")
        return;
    for(size_t i = 0; i < attr_list->getAttributeCount(); ++i)
    {
        AttributePtr attr(attr_list->getAttribute(i));
        CESAttribute* ces_attr = editable->m_CESAttributes.getAttribute(attr->getName());
        if(!ces_attr)
            THROW_EXCEPTION("Unknown attribute: " + attr->getName());
        if(attr->isContainer())
        {
            switch(attr->getType())
            {
			case CE_UVT_FLOAT: m_PropModel.insertProperty(CreateEditableProperty<DynamicArray<float>>::exec(ces_attr, attr)); break;
			case CE_UVT_VEC2: m_PropModel.insertProperty(CreateEditableProperty<DynamicArray<Vector2>>::exec(ces_attr, attr)); break;
            case CE_UVT_VEC3: m_PropModel.insertProperty(CreateEditableProperty<DynamicArray<Vector3>>::exec(ces_attr, attr)); break;
            case CE_UVT_VEC4: m_PropModel.insertProperty(CreateEditableProperty<DynamicArray<Vector4>>::exec(ces_attr, attr)); break;
            case CE_UVT_INT: m_PropModel.insertProperty(CreateEditableProperty<DynamicArray<int32>>::exec(ces_attr, attr)); break;
            case CE_UVT_UINT: m_PropModel.insertProperty(CreateEditableProperty<DynamicArray<uint32>>::exec(ces_attr, attr)); break;
            case CE_UVT_MAT4: m_PropModel.insertProperty(CreateEditableProperty<DynamicArray<Matrix4>>::exec(ces_attr, attr)); break;
            default:
                break;
            }
        }   
        else
        {
            switch(attr->getType())
            {
            case CE_UVT_FLOAT: m_PropModel.insertProperty(CreateEditableProperty<float>::exec(ces_attr, attr)); break;
            case CE_UVT_VEC2: m_PropModel.insertProperty(CreateEditableProperty<Vector2>::exec(ces_attr, attr)); break;
            case CE_UVT_VEC3: m_PropModel.insertProperty(CreateEditableProperty<Vector3>::exec(ces_attr, attr)); break;
            case CE_UVT_VEC4: m_PropModel.insertProperty(CreateEditableProperty<Vector4>::exec(ces_attr, attr)); break;
            case CE_UVT_INT: m_PropModel.insertProperty(CreateEditableProperty<int32>::exec(ces_attr, attr)); break;
            case CE_UVT_UINT: m_PropModel.insertProperty(CreateEditableProperty<uint32>::exec(ces_attr, attr)); break;
            case CE_UVT_MAT4: m_PropModel.insertProperty(CreateEditableProperty<Matrix4>::exec(ces_attr, attr)); break;
            default:
                break;
            }
        }
    }
}

QModelIndex PropertyTreeModel::parent(const QModelIndex& index) const
{
    Property* ptr = reinterpret_cast<Property*>(index.internalPointer());
    if(!(index.isValid() && ptr))
        return QModelIndex();
    Property* parent_ptr = ptr->getParent();
    if(parent_ptr)
    {
        const Property* pp_ptr = parent_ptr->getParent();
        if(pp_ptr)
        {
            size_t idx = pp_ptr->getIndex(parent_ptr);
            if(idx != std::numeric_limits<size_t>::max())
                return createIndex(idx, 0, parent_ptr);
            return QModelIndex();
        }
        for(size_t i = 0; i < m_Properties.size(); ++i)
            if(m_Properties[i].get() == parent_ptr)
                return createIndex(i, 0, parent_ptr);
        return QModelIndex();
    }
    return QModelIndex();
}

QModelIndex PropertyTreeModel::index(int row, int column, const QModelIndex& parent) const
{
    if(parent.isValid())
    {
        Property* ptr = reinterpret_cast<Property*>(parent.internalPointer());
        if(!ptr || row >= (int)ptr->getChildrenCount())
            return QModelIndex();
        return createIndex(row, column, &ptr->getChild(row));
    }
    if(row < (int)m_Properties.size())
        return createIndex(row, column, m_Properties[row].get());
    return QModelIndex();
}

QVariant PropertyTreeModel::data(const QModelIndex& index, int role) const
{
    if(!index.isValid())
        return QVariant();

    Property* ptr = reinterpret_cast<Property*>(index.internalPointer());
    return ptr->data(index.column(), role);
}

QVariant PropertyTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(role == Qt::DisplayRole && orientation == Qt::Horizontal)
        switch(section)
        {
        case PROPERTY_NAME: return QVariant("Property");
        case PROPERTY_VALUE: return QVariant("Value");
        }
    return QVariant();
}

int PropertyTreeModel::rowCount(const QModelIndex& parent) const
{
    if(parent.isValid())
    {
        Property* ptr = reinterpret_cast<Property*>(parent.internalPointer());
        return ptr->getChildrenCount();
    }
    return m_Properties.size();
}

bool PropertyTreeModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
    Property* ptr = reinterpret_cast<Property*>(index.internalPointer());
    if(!(index.isValid() && ptr))
        return false;
    return ptr->setData(value, role);
}

Qt::ItemFlags PropertyTreeModel::flags(const QModelIndex& index) const
{
    if(index.isValid())
    {
        Property* ptr = reinterpret_cast<Property*>(index.internalPointer());
        return ptr->flags(index.column());
    }
    return Qt::NoItemFlags;
}

int PropertyTreeModel::columnCount(const QModelIndex & parent) const
{
    return PROPERTY_COLUMN_COUNT;
}

bool PropertyTreeModel::insertRows(int row, int count, const QModelIndex& parent)
{
    return false;
}

bool PropertyTreeModel::removeRows(int row, int count, const QModelIndex& parent)
{
    int row_end = row + count;
    assert(!parent.isValid());
    beginRemoveRows(parent, row, row_end-1);
    m_Properties.erase(row, row_end);
    endRemoveRows();
    return true;
}

bool PropertyTreeModel::insertProperty(const PropertyPtr& _property)
{
    beginInsertRows(QModelIndex(), m_Properties.size(), m_Properties.size());
    m_Properties.push_back(_property);
    endInsertRows();
    return true;
}

void PropertyTreeModel::clear()
{
    beginResetModel();
    m_Properties.clear();
    endResetModel();
}

QWidget* PropertyTreeDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    Property* ptr = reinterpret_cast<Property*>(index.internalId());
    if(!(index.isValid() && ptr))
        return nullptr;
    switch(ptr->getType())
    {
    case CE_UVT_FLOAT:
    {
        double _min, _max;
        QDoubleValidator::Notation notation;
        switch(ptr->getRange())
        {
        case CE_RANGE_NORMALIZED:
        case CE_RANGE_CLAMPED: _min = -1.0, _max = 1.0, notation = QDoubleValidator::StandardNotation; break;
        case CE_RANGE_CLAMPED_POSITIVE: _min = 0.0, _max = 1.0, notation = QDoubleValidator::StandardNotation; break;
        default:
            _min = -FLT_MAX, _max = FLT_MAX, notation = QDoubleValidator::ScientificNotation;
        }
        QLineEdit* line_edit = CE_ALLOCATE(QLineEdit)(parent);
        QDoubleValidator* v = CE_ALLOCATE(QDoubleValidator)(_min, _max, 5, line_edit);
        v->setNotation(notation);
        line_edit->setValidator(v);
        return line_edit;
    } break;
    case CE_UVT_VEC2: return CE_ALLOCATE(Vector2Editor)(ptr->getRange(), parent); break;
    case CE_UVT_VEC3: return CE_ALLOCATE(Vector3Editor)(ptr->getRange(), parent); break;
    case CE_UVT_VEC4: return CE_ALLOCATE(Vector4Editor)(ptr->getRange(), parent); break;
    case CE_UVT_INT: return CE_ALLOCATE(QSpinBox)(parent); break;
    case CE_UVT_UINT:
    {
        QSpinBox* spin_box = CE_ALLOCATE(QSpinBox)(parent);
        spin_box->setMinimum(0);
        return spin_box;
    } break;
    default:
        break;
    }
    return nullptr;
}

void PropertyTreeDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
    Property* ptr = reinterpret_cast<Property*>(index.internalId());
    if(!(index.isValid() && ptr && editor))
        return;
    switch(ptr->getType())
    {
    case CE_UVT_FLOAT:
        static_cast<QLineEdit*>(editor)->setText(convert_value(static_cast<ValueProperty<float>*>(ptr)->getValue()).c_str()); 
    break;
    case CE_UVT_VEC2:
        static_cast<Vector2Editor*>(editor)->setValue(static_cast<ValueProperty<Vector2>*>(ptr)->getValue()); 
    break;
    case CE_UVT_VEC3:
        static_cast<Vector3Editor*>(editor)->setValue(static_cast<ValueProperty<Vector3>*>(ptr)->getValue()); 
    break;
    case CE_UVT_VEC4:
        static_cast<Vector4Editor*>(editor)->setValue(static_cast<ValueProperty<Vector4>*>(ptr)->getValue()); 
    break;
    case CE_UVT_INT:
        static_cast<QSpinBox*>(editor)->setValue(static_cast<ValueProperty<int32>*>(ptr)->getValue()); 
    break;
    case CE_UVT_UINT:
        static_cast<QSpinBox*>(editor)->setValue(static_cast<ValueProperty<uint32>*>(ptr)->getValue()); 
    break;
    default:
        break;
    }
}

void PropertyTreeDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
    Property* ptr = reinterpret_cast<Property*>(index.internalId());
    if(!(index.isValid() && ptr && editor))
        return;
    switch(ptr->getType())
    {
    case CE_UVT_FLOAT: model->setData(index, static_cast<QLineEdit*>(editor)->text().toFloat()); break;
    case CE_UVT_VEC2:
    {
        QVariant v;
        v.setValue(static_cast<Vector2Editor*>(editor)->getValue());
        model->setData(index, v);
    }  break;
    case CE_UVT_VEC3: 
    {
        QVariant v;
        v.setValue(static_cast<Vector3Editor*>(editor)->getValue());
        model->setData(index, v);
    } break;
    case CE_UVT_VEC4:
    {
        QVariant v;
        v.setValue(static_cast<Vector4Editor*>(editor)->getValue());
        model->setData(index, v);
    } break;
    case CE_UVT_INT: model->setData(index, static_cast<QSpinBox*>(editor)->value()); break;
    case CE_UVT_UINT: model->setData(index, static_cast<unsigned>(static_cast<QSpinBox*>(editor)->value())); break;
    default:
        break;
    }
}

void PropertyTreeDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    editor->setGeometry(option.rect);
}

Vector2Editor::Vector2Editor(VarRange range, QWidget* parent)
    :   QWidget(parent),
        m_IsNormalized(false)
{
    double _min, _max;
    QDoubleValidator::Notation notation;
    switch(range)
    {
    case CE_RANGE_CLAMPED: _min = -1.0, _max = 1.0, notation = QDoubleValidator::StandardNotation;  break;
    case CE_RANGE_CLAMPED_POSITIVE: _min = 0.0, _max = 1.0, notation = QDoubleValidator::StandardNotation; break;
    case CE_RANGE_NORMALIZED: m_IsNormalized = true;
    default:
        _min = -FLT_MAX, _max = FLT_MAX, notation = QDoubleValidator::ScientificNotation; break;
    }
    m_LineEdits[0] = CE_ALLOCATE(QLineEdit);
    m_LineEdits[1] = CE_ALLOCATE(QLineEdit);
    QDoubleValidator    *v1 = CE_ALLOCATE(QDoubleValidator)(_min, _max, 5, m_LineEdits[0]),
                        *v2 = CE_ALLOCATE(QDoubleValidator)(_min, _max, 5, m_LineEdits[1]);
    v1->setNotation(notation);
    v2->setNotation(notation);
    m_LineEdits[0]->setValidator(v1);
    m_LineEdits[1]->setValidator(v2);
    QHBoxLayout* layout = CE_ALLOCATE(QHBoxLayout);
    layout->setMargin(0);
    layout->addWidget(m_LineEdits[0]);
    layout->addWidget(m_LineEdits[1]);
    setLayout(layout);
}

Vector2 Vector2Editor::getValue() const
{
    Vector2 vec(m_LineEdits[0]->text().toFloat(),
                m_LineEdits[1]->text().toFloat());
    if(m_IsNormalized)
        vec.normalize();
    return vec;
}

void Vector2Editor::setValue(const Vector2& vec)
{
    QString t[2];
    t[0].setNum(vec.x());
    t[1].setNum(vec.y());
    m_LineEdits[0]->setText(t[0]);
    m_LineEdits[1]->setText(t[1]);
}

Vector3Editor::Vector3Editor(VarRange range, QWidget* parent)
    :   QWidget(parent),
        m_IsNormalized(false)
{
    double _min, _max;
    QDoubleValidator::Notation notation;
    switch(range)
    {
    case CE_RANGE_CLAMPED: _min = -1.0, _max = 1.0, notation = QDoubleValidator::StandardNotation;  break;
    case CE_RANGE_CLAMPED_POSITIVE: _min = 0.0, _max = 1.0, notation = QDoubleValidator::StandardNotation; break;
    case CE_RANGE_NORMALIZED: m_IsNormalized = true;
    default:
        _min = -FLT_MAX, _max = FLT_MAX, notation = QDoubleValidator::ScientificNotation; break;
    }
    m_LineEdits[0] = CE_ALLOCATE(QLineEdit);
    m_LineEdits[1] = CE_ALLOCATE(QLineEdit);
    m_LineEdits[2] = CE_ALLOCATE(QLineEdit);
    QDoubleValidator    *v1 = CE_ALLOCATE(QDoubleValidator)(_min, _max, 5, m_LineEdits[0]),
                        *v2 = CE_ALLOCATE(QDoubleValidator)(_min, _max, 5, m_LineEdits[1]),
                        *v3 = CE_ALLOCATE(QDoubleValidator)(_min, _max, 5, m_LineEdits[2]);
    v1->setNotation(notation);
    v2->setNotation(notation);
    v3->setNotation(notation);
    m_LineEdits[0]->setValidator(v1);
    m_LineEdits[1]->setValidator(v2);
    m_LineEdits[2]->setValidator(v3);
    QHBoxLayout* layout = CE_ALLOCATE(QHBoxLayout);
    layout->setMargin(0);
    layout->addWidget(m_LineEdits[0]);
    layout->addWidget(m_LineEdits[1]);
    layout->addWidget(m_LineEdits[2]);
    setLayout(layout);
}

Vector3 Vector3Editor::getValue() const
{
    Vector3 vec(m_LineEdits[0]->text().toFloat(),
                m_LineEdits[1]->text().toFloat(),
                m_LineEdits[2]->text().toFloat());
    if(m_IsNormalized)
        vec.normalize();
    return vec;
}

void Vector3Editor::setValue(const Vector3& vec)
{
    QString t[3];
    t[0].setNum(vec.x());
    t[1].setNum(vec.y());
    t[2].setNum(vec.z());
    m_LineEdits[0]->setText(t[0]);
    m_LineEdits[1]->setText(t[1]);
    m_LineEdits[2]->setText(t[2]);
}

Vector4Editor::Vector4Editor(VarRange range, QWidget* parent)
    :   QWidget(parent),
        m_IsNormalized(false)
{
    double _min, _max;
    QDoubleValidator::Notation notation;
    switch(range)
    {
    case CE_RANGE_CLAMPED: _min = -1.0, _max = 1.0, notation = QDoubleValidator::StandardNotation;  break;
    case CE_RANGE_CLAMPED_POSITIVE: _min = 0.0, _max = 1.0, notation = QDoubleValidator::StandardNotation; break;
    case CE_RANGE_NORMALIZED: m_IsNormalized = true;
    default:
        _min = -FLT_MAX, _max = FLT_MAX, notation = QDoubleValidator::ScientificNotation; break;
    }
    m_LineEdits[0] = CE_ALLOCATE(QLineEdit);
    m_LineEdits[1] = CE_ALLOCATE(QLineEdit);
    m_LineEdits[2] = CE_ALLOCATE(QLineEdit);
    m_LineEdits[3] = CE_ALLOCATE(QLineEdit);
    QDoubleValidator *v1 = CE_ALLOCATE(QDoubleValidator)(_min, _max, 5, m_LineEdits[0]),
                     *v2 = CE_ALLOCATE(QDoubleValidator)(_min, _max, 5, m_LineEdits[1]),
                     *v3 = CE_ALLOCATE(QDoubleValidator)(_min, _max, 5, m_LineEdits[2]);
    v1->setNotation(notation);
    v2->setNotation(notation);
    v3->setNotation(notation);
    m_LineEdits[0]->setValidator(v1);
    m_LineEdits[1]->setValidator(v2);
    m_LineEdits[2]->setValidator(v3);
    m_LineEdits[3]->setValidator(CE_ALLOCATE(QDoubleValidator)(-1.0, 1.0, 5, 0));   
    QHBoxLayout* layout = CE_ALLOCATE(QHBoxLayout);
    layout->setMargin(0);
    layout->addWidget(m_LineEdits[0]);
    layout->addWidget(m_LineEdits[1]);
    layout->addWidget(m_LineEdits[2]);
    layout->addWidget(m_LineEdits[3]);
    setLayout(layout);
}

Vector4 Vector4Editor::getValue() const
{
    Vector4 vec(m_LineEdits[0]->text().toFloat(),
                m_LineEdits[1]->text().toFloat(),
                m_LineEdits[2]->text().toFloat(),
                m_LineEdits[3]->text().toFloat());
    if(m_IsNormalized)
        vec.xyz().normalize(), vec.w() = 1.0f;
    return vec;
}

void Vector4Editor::setValue(const Vector4& vec)
{
    QString t[4];
    t[0].setNum(vec.x());
    t[1].setNum(vec.y());
    t[2].setNum(vec.z());
    t[3].setNum(vec.w());
    m_LineEdits[0]->setText(t[0]);
    m_LineEdits[1]->setText(t[1]);
    m_LineEdits[2]->setText(t[2]);
    m_LineEdits[3]->setText(t[3]);
}

